Promise入门

Promise是什么

在MDN上是这样解释的:

  • The Promise object is used for asynchronous computations.(Promise 对象用于异步计算)
  • The Promise object represents the eventual completion (or failure) of an asynchronous operation, and its resulting value.(Promise 对象用于表示一个异步操作的最终状态(完成或失败),以及其返回的值。)

看完上面的解释你可能还是不太明白,我个人来理解,按照用途可以这样解释:

  1. 主要用于异步计算
  2. 可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果
  3. 可以在对象之间传递和操作 Promise ,帮助我们处理队列

异步回调存在的问题

我们在使用js做开发的时候,存在非常多的异步操作,而异步回调也是家常便饭,而在异步回调中存在着四个问题:

  1. 嵌套层很深,难以维护
  2. 无法正常使用reutrn 和 throw
  3. 无法正常检索堆栈信息
  4. 多个回调之间难以建立联系

Promise 简介

直接上代码解释:

1
2
3
4
5
6
7
8
9
10
11
12
13
new Promise(
//执行器 executor
function(reslove,reject) {
//一段耗时的异步操作
reslove();//数据处理完成
reject();//数据处理出错
}
)
.then(function A() {
//成功,下一步
},function B(){
//失败,做相应的处理
})

以上就是 Promise 的语法,可以看出:

  • Promise 是一个代理对象,它和原先要进行的操作并无关系
  • 它通过引入一个回调,来避免更多的回调

Promise有三个状态:

  • pending,待定,初始状态
  • fulfilled,实现,成功状态
  • reject,被否决,失败状态

当 Promise 状态发生改变,就会触发 .then() 里的响应函数处理后续步骤;此外,Promise 的状态一经改变,是不会再改变的。

如图,在 Promise 中,每一个 then() 都会返回一个新的 Promise ,这样就由 then() 的调用生产一个 Promise 的调用队列,根据执行结果的状态,不断的调用 then() 里面对应的处理函数,一个 then() 处理完后,继续下一个,直到所有的处理函数执行完毕。

使用promise基本步骤(2步):

  1. 创建promise对象
1
2
3
4
5
6
7
8
9
let promise = new Promise((resolve, reject) => {
//初始化promise状态为 pending
//执行异步操作
if(异步操作成功) {
resolve(value);//修改promise的状态为fullfilled
} else {
reject(errMsg);//修改promise的状态为rejected
}
})

调用promise的then():

1
2
3
4
promise.then(function(
result => console.log(result),
errorMsg => alert(errorMsg)
))

promise 的基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//基本使用
function loadImg(src) {
var promise = new Promise(function (resolve, reject) {
var img = document.createElement('img')
//throw new Error("图片加载失败")//模仿语法报错
img.onload = function () {
resolve(img)
}
img.onerror = function () {
reject('图片加载失败')
}
img.src = src
})
return promise
}

var src = 'https://www.imooc.com/static/img/index/logo_new.png'
var result = loadImg(src)
result.then(function (img) {
console.log(1, img.width)
return img // 非promise对象会自动包装成一个 promise 对象,并且img会作为参数传递,类似于:resolve(img)
}, function () {
console.log('error 1')
}).then(function (img) {
console.log(2, img.height)
return img
},function(){
console.log('error 2')
})
1
2
3
4
5
6
7
8
9
10
11
12
// 统一捕获异常
var src = 'https://www.imooc.com/static/img/index/logo_new.png'
var result = loadImg(src)
result.then(function (img) {
console.log(1, img.width)
return img
}).then(function (img) {
console.log(2, img.height)
}).catch(function (ex) {
// 统一捕获异常
console.log(ex)
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//多个串联,第一个加载完后加载第二个,以此类推
var src1 = 'https://www.imooc.com/static/img/index/logo_new.png'
var result1 = loadImg(src1)
var src2 = 'https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg'
var result2 = loadImg(src2)
result1.then(function (img1) {
console.log('第一个图片加载完成', img1.width)
return result2 // 重要!!!
}).then(function (img2) {
console.log('第二个图片加载完成', img2.width)
}).catch(function (ex) {
//统一catch
console.log(ex)
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//promise.all 接收一个 promise 对象的数组
//待全部完成后,统一执行 success
var src1 = 'https://www.imooc.com/static/img/index/logo_new.png'
var result1 = loadImg(src1)
var src2 = 'https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg'
var result2 = loadImg(src2)
Promise.all([result1, result2]).then(function (datas) {
//接收到的 datas 是一个数组,依次包含了多个 promise 返回内容
console.log('all', datas[0])
console.log('all', datas[1])
})
//promise.race 接收一个包含多个 promise对象数组
//只要有一个完成,就执行 success
Promise.race([result1, result2]).then(function (data) {
//data 即最先执行完成的 promises 的返回值
console.log('race', data)
})

实际应用

promise 用的最广泛的地方在两个:

  1. 使用promise实现超时处理
  2. 使用promise封装处理ajax请求
1
2
3
4
5
let request = new XMLHttpRequest();
request.onreadystatechange = function () {}
request.responseType = 'json';
request.open("GET", url);
request.send();

来看看 promise 的执行顺序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//创建一个promise实例对象
let promise = new Promise((resolve, reject) => {
//初始化promise的状态为pending---->初始化状态
console.log('1111');//同步执行
//启动异步任务
setTimeout(function () {
console.log('3333');
//resolve('atguigu.com');//修改promise的状态pending---->fullfilled(成功状态)
reject('xxxx');//修改promise的状态pending----->rejected(失败状态)
},1000)
});
promise.then((data) => {
console.log('成功了。。。' + data);
}, (error) => {
console.log('失败了' + error);
});
console.log('2222');

执行结果:1111 => 2222 => 3333 => 失败了xxxx(成功了。。。atguigu.com)

可以用下图表示执行过程:

promise过程分析

完整使用案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//定义一个请求news的方法
function getNews(url) {
//创建一个promise对象
let promise = new Promise((resolve, reject) => {
//初始化promise状态为pending
//启动异步任务
let request = new XMLHttpRequest();
request.onreadystatechange = function () {
if(request.readyState === 4){
if(request.status === 200){
let news = request.response;
resolve(news);
}else{
reject('请求失败了。。。');
}
}
};
request.responseType = 'json';//设置返回的数据类型
request.open("GET", url);//规定请求的方法,创建链接
request.send();//发送
})
return promise;
}

getNews('http://localhost:3000/news?id=2')
.then((news) => {
console.log(news);
document.write(JSON.stringify(news));
console.log('http://localhost:3000' + news.commentsUrl);
return getNews('http://localhost:3000' + news.commentsUrl);
}, (error) => {
alert(error);
//reject("error")
//return 1;
//return error
//return new promise();
})
.then((comments) => {
console.log(comments);
document.write('<br><br><br><br><br>' + JSON.stringify(comments));
}, (error) => {
alert(error);
})

运行代码后你会发现:

第一个promise如果成功,返回的是一个 promise,那么第二个 then 则看这个 promise 的结果。由于当前本地没有开启服务器接口,所以你第一个是错误的 reject('请求失败了。。。'),那么第二个 then 看的就是第一个 then 中 error 函数的返回,你只 alert,默认函数返回不是返回一个promise 对象,所以第二个 then 在你第一个 promise 进去错误的情况下,肯定是会执行的,并且 comments 为 undefined。

总的来说:promise 在 then 之后的后继的 then 都是看前面的 then 中执行的那个函数的结果来进行相应的操作,基本就两个,返回不是 promise 类型的,和是 promise 类型的,如果是 promise 类型的,则这个 then 就是基本的 then 操作,如果不是,那应该就是简单的带参回调函数。

注意:如果忽略针对某个状态的回调函数参数,或者提供非函数 (nonfunction) 参数,那么 then 方法将会丢失关于该状态的回调函数信息,但是并不会产生错误。如果调用 then 的 Promise 的状态(fulfillment 或 rejection)发生改变,但是 then 中并没有关于这种状态的回调函数,那么 then 将创建一个没有经过回调函数处理的新 Promise 对象,这个新 Promise 只是简单地接受调用这个 then 的原 Promise 的终态作为它的终态。

并且:如果then中的回调函数返回一个值,那么then返回的Promise将会成为接受状态,并且将返回的值作为接受状态的回调函数的参数值。

具体请参考: Promise.prototype.then()



完~